/**
 * PhongI.cpp
 *
 * Author:
 *   Chris Herbison
 *
 * Description:
 *   Class that simulates the Phong Illumination model.
 */

#include "PhongI.h"
#include "Ray.h"
#include <cmath>

/*
 * The constructor.
 *
 * _ka - Background light constant of object.
 * _ks - Reflection constant of object.
 * _kd - Diffuse constant of object.
 * _ke - Specular highlight constant.
 * _kr - Reflective constant.
 * _kt - Transmissive constant.
 */
PhongI::PhongI(/*float _ka, float _ks, float _kd, float _ke, float _kr, float _kt*/): 
                 IlluminationModel()/*, ka(_ka), ks(_ks), kd(_kd), ke(_ke), kr(_kr), kt(_kt)*/{
}

/*
 * Applies the Phong Illumation model on the objects give the intersection data.
 *
 * id - The information taken from the intersection of the ray and the object.
 *
 * return (MyColor) - The color to draw this pixel.
 */
MyColor PhongI::illuminate(IntersectData id, Object* originalIntersect, WorldData wd, int index, int depth){
     //Check if there is a clear path to each light.
     vector<bool> clear(wd.lights.size(), false);
     IntersectData dummyData;
     for(int i = 0; i < (int)wd.lights.size(); i++){
          Point3 tempLPos(wd.lights.at(i).position);
          Vector3 newDir(tempLPos - id.pos);
          for(int j = 0; j < (int)wd.objects.size(); j++){
			  if(wd.objects.at(j) != originalIntersect && wd.objects.at(j)->intersects(Ray(id.pos, newDir), &dummyData)){
				  if(id.pos.distanceToSquared(dummyData.pos) < id.pos.distanceToSquared(tempLPos)){
					j = wd.objects.size();
				  }
				  else if(j == (wd.objects.size()-1)){
					  clear.at(i) = true;
				  }
              }
              else if(j == (wd.objects.size()-1)){
                   clear.at(i) = true;
              }
          }
     }
     IlluminationData material(originalIntersect->material);
     id.view.normalize();
     id.normal.normalize();
     double r, g, b, a;
     r = g = b = 0;
     MyColor cA, cD, cS;
     cA = id.CA;
     cD = id.CD;
     cS = id.CS;
     Vector3 ptToLight;
     a = cA.a;
     
     //Add ambient component to color.
     r += material.ka*(1*cA.r);
     g += material.ka*(1*cA.g);
     b += material.ka*(1*cA.b);
     
     //For each light, if object is lit, calculate the diffuse and specular components.
     for(int i = 0; i < (int)wd.lights.size(); i++){
          if(clear.at(i)){
               Vector3 shadow(wd.lights.at(i).position - id.pos);
               shadow.normalize();
               Vector3 tempArg((2*(shadow*id.normal))*id.normal);
               Vector3 reflect(shadow-tempArg);
               
               double LdR = wd.lights.at(i).color.r * cD.r;
               double LdG = wd.lights.at(i).color.g * cD.g;
               double LdB = wd.lights.at(i).color.b * cD.b;
               double shadowDotNormal = (shadow*id.normal);
               if(shadowDotNormal >= 0){
                    r += material.kd*LdR*shadowDotNormal;
                    g += material.kd*LdG*shadowDotNormal;
                    b += material.kd*LdB*shadowDotNormal;
               }
               
               double LsR = wd.lights.at(i).color.r * cS.r;
               double LsG = wd.lights.at(i).color.g * cS.g;
               double LsB = wd.lights.at(i).color.b * cS.b;
               double exponent = pow((double)(reflect*id.view), (double)material.ke);
               if(exponent >= 0){
                    r += material.ks*LsR*exponent;
                    g += material.ks*LsG*exponent;
                    b += material.ks*LsB*exponent;
               }
          }
     }

     //If object is reflective or transmissive, do recursive shading.
     if(depth < MAX_DEPTH){
          if(material.kr != 0){
               IntersectData nextIntersect;
               int nextIndex = -1;
               for(int i = 0; i < (int)wd.objects.size(); i++){
                    if((*(wd.objects.at(i))).intersects(Ray(id.pos, id.reflective), &nextIntersect) && i != index){
                         nextIndex = i;
                         i = wd.objects.size();
                    }
               }
               MyColor tempColor;
               if(nextIndex != -1){
                    tempColor = illuminate(nextIntersect, wd.objects.at(nextIndex), wd, nextIndex, depth+1);
               }
               else{
                    tempColor = BG_COLOUR;
               }
               r += material.kr*tempColor.r;
               g += material.kr*tempColor.g;
               b += material.kr*tempColor.b;
          }
          if(material.kt != 0){
               int nextIndex = -1;
               IntersectData nextIntersect;
               for(int i = 0; i < (int)wd.objects.size(); i++){
                    if((*(wd.objects.at(i))).intersects(Ray(id.pos, id.transmissive), &nextIntersect) && i != index){
                         nextIndex = i;
                         i = wd.objects.size();
                    }
               }
               MyColor tempColor;
               if(nextIndex == -1){
                    tempColor = BG_COLOUR;
               }
               else{
				   tempColor = illuminate(nextIntersect, wd.objects.at(nextIndex), wd, nextIndex, depth+1);
               }
               r += material.kt*tempColor.r;
               g += material.kt*tempColor.g;
               b += material.kt*tempColor.b;
          }
     }
     
     return MyColor(r,g,b,a);
}
